package ar.edu.itba.tp1.tpmariano;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import ar.edu.itba.tp1.gps.api.GPSProblem;
import ar.edu.itba.tp1.gps.api.GPSRule;
import ar.edu.itba.tp1.gps.api.GPSState;
import ar.edu.itba.tp1.gps.exception.NotAppliableException;

public class Problem implements GPSProblem {

	protected static final int LENGTH = 4;

	private GPSState initState;
	private List<GPSRule> rules;

	public Problem() {
		this.initState = buildInitState();
		this.rules = new ArrayList<GPSRule>();

		/* Rotation rules for each inner (not edge) tile */
		for (int i = 1; i < LENGTH - 1; i++)
			for (int j = 1; j < LENGTH - 1; j++)
				this.rules.add(new RotateGPSRule(i, j));

		/* Swapping rules for corners (listed counter-clockwise) */
		this.rules.add(new CornerSwapGPSRule(0, 0, 0, 3));
		this.rules.add(new CornerSwapGPSRule(0, 0, 3, 3));
		this.rules.add(new CornerSwapGPSRule(0, 0, 3, 0));

		this.rules.add(new CornerSwapGPSRule(0, 3, 3, 3));
		this.rules.add(new CornerSwapGPSRule(0, 3, 3, 0));

		this.rules.add(new CornerSwapGPSRule(3, 3, 3, 0));

		/* Swapping rules for borders (not corners) (listed counter-clockwise) */
		this.rules.add(new BorderSwapGPSRule(0, 1, 0, 2));
		this.rules.add(new BorderSwapGPSRule(0, 1, 1, 3));
		this.rules.add(new BorderSwapGPSRule(0, 1, 2, 3));
		this.rules.add(new BorderSwapGPSRule(0, 1, 3, 2));
		this.rules.add(new BorderSwapGPSRule(0, 1, 3, 1));
		this.rules.add(new BorderSwapGPSRule(0, 1, 2, 0));
		this.rules.add(new BorderSwapGPSRule(0, 1, 1, 0));

		this.rules.add(new BorderSwapGPSRule(0, 2, 1, 3));
		this.rules.add(new BorderSwapGPSRule(0, 2, 2, 3));
		this.rules.add(new BorderSwapGPSRule(0, 2, 3, 2));
		this.rules.add(new BorderSwapGPSRule(0, 2, 3, 1));
		this.rules.add(new BorderSwapGPSRule(0, 2, 2, 0));
		this.rules.add(new BorderSwapGPSRule(0, 2, 1, 0));

		this.rules.add(new BorderSwapGPSRule(1, 3, 2, 3));
		this.rules.add(new BorderSwapGPSRule(1, 3, 3, 2));
		this.rules.add(new BorderSwapGPSRule(1, 3, 3, 1));
		this.rules.add(new BorderSwapGPSRule(1, 3, 2, 0));
		this.rules.add(new BorderSwapGPSRule(1, 3, 1, 0));

		this.rules.add(new BorderSwapGPSRule(2, 3, 3, 2));
		this.rules.add(new BorderSwapGPSRule(2, 3, 3, 1));
		this.rules.add(new BorderSwapGPSRule(2, 3, 2, 0));
		this.rules.add(new BorderSwapGPSRule(2, 3, 1, 0));

		this.rules.add(new BorderSwapGPSRule(3, 2, 3, 1));
		this.rules.add(new BorderSwapGPSRule(3, 2, 2, 0));
		this.rules.add(new BorderSwapGPSRule(3, 2, 1, 0));

		this.rules.add(new BorderSwapGPSRule(3, 1, 2, 0));
		this.rules.add(new BorderSwapGPSRule(3, 1, 1, 0));

		this.rules.add(new BorderSwapGPSRule(2, 0, 1, 0));

		/* Swapping rules for center tiles (listed counter-clockwise) */
		this.rules.add(new SwapGPSRule(1, 1, 1, 2));
		this.rules.add(new SwapGPSRule(1, 1, 2, 2));
		this.rules.add(new SwapGPSRule(1, 1, 2, 1));

		this.rules.add(new SwapGPSRule(1, 2, 2, 2));
		this.rules.add(new SwapGPSRule(1, 2, 2, 1));

		this.rules.add(new SwapGPSRule(2, 2, 2, 1));
	}

	@Override
	public List<GPSRule> getRules() {
		return this.rules;
	}

	@Override
	public Integer getHValue(GPSState state) {
		// TODO Auto-generated method stub
		return null;
	}

	public void randomize(int iterations, int seed) {

		Random random = new Random(seed);
		for (int i = 0; i < iterations; i++) {
			int index = (int) (random.nextFloat() * rules.size());
			GPSRule rule = rules.get(index);
			try {
				initState = rule.evalRule(initState);
			} catch (NotAppliableException e) {
			}
		}
	}

	@Override
	public GPSState getInitState() {
		return this.initState;
	}

	private GPSState buildInitState() {
		Grid init = new Grid();

		/* Add corners (added clockwise from (0,3)) */
		init.putTile(0, 3, 0, 1, 1, 0);
		init.putTile(3, 3, 0, 0, 2, 2);
		init.putTile(3, 0, 2, 0, 0, 1);
		init.putTile(0, 0, 2, 1, 0, 0);

		/* Add borders (added clockwise starting from (1,3)) */
		init.putTile(1, 3, 0, 2, 3, 1);
		init.putTile(2, 3, 0, 1, 3, 2);
		init.putTile(3, 2, 2, 0, 2, 3);
		init.putTile(3, 1, 2, 0, 2, 4);
		init.putTile(2, 0, 4, 1, 0, 2);
		init.putTile(1, 0, 4, 2, 0, 1);
		init.putTile(0, 1, 1, 4, 1, 0);
		init.putTile(0, 2, 1, 3, 1, 0);

		/* Add the rest */
		init.putTile(1, 2, 4, 3, 4, 4);
		init.putTile(2, 2, 3, 3, 3, 4);
		init.putTile(1, 1, 3, 3, 4, 4);
		init.putTile(2, 1, 3, 3, 4, 4);

		return init;
	}

	@Override
	public GPSState getGoalState() {
		// TODO Auto-generated method stub
		return null;
	}
}
